#include "cetris.h"
#include "cetris.h"

#include <iostream>

#include "./ui_cetris.h"
#include <QPaintEvent>
#include <QPainter>
#include "define.h"
#include <QMessageBox>
/**
 * \brief 19种方块，在4*4的数组中表示
 */

static constexpr std::array<block_array, 19> BLOCK = {
    {
        {
            {
                {0, 1, 1, 0},
                {0, 1, 1, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            }
        },
        {
            {
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 1, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {1, 1, 1, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {0, 1, 0, 0},
                {0, 1, 1, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 1, 0},
                {0, 1, 0, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {0, 1, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 0, 1, 0},
                {0, 1, 1, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {1, 1, 1, 1},
                {0, 0, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {0, 1, 1, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {1, 1, 1, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {1, 1, 1, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {1, 1, 0, 0},
                {0, 1, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 1, 1},
                {0, 1, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 0, 0, 1},
                {0, 1, 1, 1},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 1, 1},
                {0, 0, 0, 1},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 0, 1, 0},
                {0, 0, 1, 0},
                {0, 1, 1, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 1, 0, 0},
                {0, 1, 1, 1},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        },
        {
            {
                {0, 0, 1, 1},
                {0, 1, 1, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
            },
        }
    }
};


cetris::cetris(QWidget* parent)
    : QMainWindow(parent)
      , ui(new Ui::cetris) {
    ui->setupUi(this);
    this->setGeometry({600, 300, 540, 700});
}

cetris::~cetris() {
    delete ui;
}

void cetris::paintEvent(QPaintEvent* event) {
    QPainter painter(this);
    std::shared_ptr<nullptr_t> shared_ptr(nullptr, [&painter](auto) {
        painter.end();
    });
    if (!game_started) {
        init_block();
    }

    painter.setPen(Qt::white);
    painter.setBrush(Qt::gray);
    painter.drawRect(20, 20, 500, 620); //外边框

    painter.setPen(Qt::gray);
    painter.setBrush(Qt::white);
    painter.drawRect(30, 30, 480, 600); //内背景

    painter.setPen(Qt::gray);
    for (int i = 70; i < 630; i = i + 40) {
        painter.drawLine(30, i, 510, i);
    } //画横线
    for (int i = 70; i < 510; i = i + 40) {
        painter.drawLine(i, 30, i, 630);
    } //画竖线

    painter.setPen(Qt::black); //画方块
    painter.setBrush(Qt::red);
    std::for_each(blocks.begin(), blocks.end(), [&painter](const QRect&e) {
        painter.drawPixmap(e, QPixmap(":/float.png"));
    });

    for (int i = 0; i < ROW; i++) {
        //画停下来的方块
        for (int j = 0; j < COL; j++) {
            if (background[i][j] == 1) {
                painter.setPen(Qt::black);
                painter.setBrush(Qt::green);
                painter.drawPixmap(30 + j * 40, 30 + i * 40, 40, 40, QPixmap(":/fixed.png"));
            }
        }
    }

    //显示游戏开始和结束
    QFont font1("Courier", 24);
    painter.setFont(font1);
    painter.setPen(Qt::white);
    painter.setBrush(Qt::blue);
    painter.drawText(20, 670, "得分：");
    painter.drawText(100, 670, QString::number(score));

    if (cur_over) {
        timer->stop();
        delete timer;
        std::for_each(blocks.begin(), blocks.end(), [&](QRect&e) {
            background[(e.top() - 30) / 40][(e.left() - 30) / 40] = 1;
        });
        for (int i = 0; i < ROW; i++) {
            bool need_clear = true;
            for (int j = 0; j < COL; j++) {
                if (background[i][j] == 0) {
                    need_clear = false;
                    break;
                }
            }
            if (need_clear) {
                for (auto&e: background[i]) {
                    e = 0;
                }
                score += 10;
                for (int j = i; j > 0; j--) {
                    background[j] = background[j - 1];
                }
            }
        }
        if (game_has_over()) {
            cur_over=false;
            std::string msg = "您的游戏分数是：" + std::to_string(score);
            QString qmsg = msg.data();
            QMessageBox::information(this, "游戏结束", qmsg);
            close();

        }
        else {
            init_block();
        }
    }
}

void cetris::init_block() {
    init_temp_block();
    type = rand() % 19;
    game_started = true;
    cur_over = false;
    create_block();
    timer = new QTimer(this);
    timer->start(speed);
    connect(timer, SIGNAL(timeout()), SLOT(block_update()));
}

void cetris::init_temp_block() noexcept {
    temp_block = BLOCK[type];
}

void cetris::create_block() {
    for (int i = 0; i < blocks.size(); i++) {
        init_rect(i);
    }
}

bool cetris::game_has_over() {
    for (int i = 0; i < COL; i++) {
        if (background[0][i] == 1) {
            return true;
        }
    }
    return false;
}

void cetris::init_rect(int i) {
    for (int j = 0; j < temp_block.size(); j++) {
        for (int k = 0; k < temp_block[k].size(); k++) {
            if (temp_block[j][k] == 1) {
                blocks[i] = QRect(190 + k * 40, 30 + j * 40 - 40, 40, 40);
                temp_block[j][k] = 0;
                return;
            }
        }
    }
}

void cetris::block_down_shift() {
    for (auto&e: blocks) {
        e.setTop(e.top() + 40);
        e.setBottom(e.bottom() + 40);
    }

    for (auto&e: blocks) {
        if (e.bottom() > 620 || background[(e.top() - 30) / 40 + 1][(e.left() - 30) / 40] == 1) {
            cur_over = true;
            break;
        }
    }
    update();
}

void cetris::block_left_shift() {
    bool can_shift = true;
    for (auto&e: blocks) {
        if (e.left() < 70 || background[(e.top() - 30) / 40 + 1][(e.left() - 30) / 40 - 1] == 1) {
            can_shift = false;
            break;
        }
    }
    if (can_shift) {
        for (auto&e: blocks) {
            e.setLeft(e.left() - 40);
            e.setRight(e.right() - 40);
        }
    }

    update();
}

void cetris::block_right_shift() {
    bool can_shift = true;
    for (auto&e: blocks) {
        if (e.left() > 430 || background[(e.top() - 30) / 40 + 1][(e.left() - 30) / 40 + 1] == 1) {
            can_shift = false;
            break;
        }
    }
    if (can_shift) {
        for (auto&e: blocks) {
            e.setLeft(e.left() + 40);
            e.setRight(e.right() + 40);
        }
    }

    update();
}

void cetris::block_update() {
    block_down_shift();
}

void cetris::keyPressEvent(QKeyEvent* event) {
    if(game_has_over() == true) {
        return;
    }
    QKeyEvent* key = event;
    switch (key->key()) {
        case Qt::Key_Down: {
            block_update();
            break;
        }
        case Qt::Key_Left: {
            block_left_shift();
            break;
        }
        case Qt::Key_Right: {
            block_right_shift();
            break;
        }
        case Qt::Key_Space: {
            if (!skip) {
                timer->setInterval(speed / 2);
                skip = true;
            }
            else {
                timer->setInterval(speed);
                skip = false;
            }
            break;
        }
        default:
            break;
    }
}
